package com.app.system.jpa;

import org.hibernate.transform.Transformers;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * PROPAGATION_REQUIRED -- 支持当前事务，如果当前没有事务，就新建一个事务。这是最常见的选择。 
 * PROPAGATION_SUPPORTS -- 支持当前事务，如果当前没有事务，就以非事务方式执行。 
 * PROPAGATION_MANDATORY -- 支持当前事务，如果当前没有事务，就抛出异常。 
 * PROPAGATION_REQUIRES_NEW -- 新建事务，如果当前存在事务，把当前事务挂起。 
 * PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作，如果当前存在事务，就把当前事务挂起。 
 * PROPAGATION_NEVER -- 以非事务方式执行，如果当前存在事务，则抛出异常。 
 * PROPAGATION_NESTED -- 如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则进行与PROPA
 * Created by wcf-pc on 2018/12/12.
 *
 * @param <T> 实体类
 * @param <L> 主键
 */
@SuppressWarnings("all")
public class JpaBaseDao<T, L> {
    private Class<T> clazz;

    public JpaBaseDao() {
        ParameterizedType t = (ParameterizedType) this.getClass().getGenericSuperclass();
        //获取泛型参数的实际类型
        this.clazz = (Class<T>) t.getActualTypeArguments()[0];
    }

    @PersistenceContext()
    protected EntityManager entityManager;

    /**
     * 保存
     *
     * @param bean
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(T bean) {
        entityManager.persist(bean);
    }

    /**
     * 更新
     *
     * @param bean
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void update(T bean) {
        entityManager.merge(bean);
    }

    /**
     * 查找
     *
     * @param id
     * @return
     */
    public T findOne(L id) {
        return entityManager.find(clazz, id);
    }

    /**
     * 根据id三处指定数据
     *
     * @param id
     * @return 小于0 表示删除失败 大与0表示删除成功
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public int delete(L id) {
        Query query = entityManager.createQuery("delete from " + clazz.getSimpleName() + " p where p.id = ?1");
        query.setParameter(1, id);
        return query.executeUpdate();
    }

    /**
     * 显示所有数据
     *
     * @return
     */
    public List<T> findAll() {
        String hql = "select t from " + clazz.getSimpleName() + " t";
        Query query = entityManager.createQuery(hql);
        List<T> beans = query.getResultList();
        return beans;
    }

    /**
     * 批量保存
     *
     * @param beans
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void batchSave(List<T> beans) {
        if (beans != null) {
            for (T bean : beans) {
                entityManager.persist(bean);
            }
            entityManager.flush();
            entityManager.clear();
        }

    }

    /**
     * 批量更新所有字段
     *
     * @param beans
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void batchUpdate(List<T> beans) {
        if (beans != null) {
            for (T bean : beans) {
                entityManager.merge(bean);
            }
            entityManager.flush();
            entityManager.clear();
        }
    }


    /**
     * 批量更新非空字段
     *
     * @param beans
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void batchUpdateNotNull(List<T> beans) {
        if (beans == null || beans.size() == 0) {
            return;
        }
        List<Map<String, Object>> datas = new LinkedList<>();
        for (T bean : beans) {
            Map<String, Object> data = ReflectUtils.createMapForNotNull(bean);
            datas.add(data);
        }

        for (Map<String, Object> map : datas) {
            //拼接Hql语句
            StringBuffer hql = new StringBuffer("update " + clazz.getSimpleName() + "");
            Set<String> keys = map.keySet();
            boolean fist = true;
            for (String key : keys) {
                if ("id".equals(key)) {
                    continue;
                }
                if (fist) {
                    hql.append(" set ").append(key + "=:" + key);
                    fist = false;
                } else {
                    hql.append("," + key + "=:" + key);
                }
            }
            hql.append(" where id=:id");
            Query query = entityManager.createQuery(hql.toString());

            //设置参数
            for (String key : keys) {
                Object value = map.get(key);
                query.setParameter(key, value);
            }
            query.executeUpdate();
        }
        entityManager.flush();
        entityManager.clear();

    }

    /**
     * 执行更新、删除sql语句
     *
     * @param sql
     * @param params
     * @return
     */
    public int executeSql(String sql, Map<String, Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        setQueryParameters(query, params);
        return query.executeUpdate();
    }

    /**
     * 执行更新、删除hql语句
     *
     * @param hql
     * @param params
     * @return
     */
    public int executeHql(String hql, Map<String, Object> params) {
        Query query = entityManager.createQuery(hql);
        setQueryParameters(query, params);
        return query.executeUpdate();
    }

    /**
     * 根据原始sql语句执行sql
     *
     * @param sql    原始sql语句
     * @param params 要传递的参数
     * @return
     */
    public List<Map> findSql(String sql, Map<String, Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        setQueryParameters(query, params);
        return query.unwrap(org.hibernate.SQLQuery.class)
                .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
    }

//    public <T1> List<T1> findSql(String sql, Class<T1> clazz, Map<String, Object> params) {
//        Query query = entityManager.createNativeQuery(sql);
//        setQueryParameters(query, params);
//        query.unwrap(clazz);
//        return query.getResultList();
//    }

    public List findSqlObject(String sql, Map<String, Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        setQueryParameters(query, params);
        return query.getResultList();
    }

    private Integer locationFrom(String sql) {
        if (sql == null || "".equals(sql)) {
            return -1;
        }
        int count = countSelect(sql);
        if (count == 0) {
            return -1;
        }
        if (count == 1) {
            return sql.indexOf("from");
        } else {
            int index = sql.indexOf("from");
            String temp = sql.substring(0, index);
            if (!temp.contains("select")) {
                return index;
            }

        }
        return null;

    }

    private int countSelect(String sql) {
        if (sql.indexOf("select") < 0) {
            return 0;
        }
        int index = sql.indexOf("select");
        return 1 + countSelect(sql.substring(index + 6));
    }

    /**
     * sql分页显示数据
     *
     * @param wrapper
     * @return
     */
    public PageBean<Map<String, Object>> pageSql(Wrapper wrapper) {
        String sql = wrapper.getQuery();
        Map<String, Object> params = wrapper.getParams();
        PageBean searchPageBean = wrapper.getPageBean();
        Integer pageNum = searchPageBean.getPageNum();
        Integer pageSize = searchPageBean.getPageSize();

        PageBean<Map<String, Object>> pageBean = new PageBean<>();
        pageBean.setPageNum(pageNum);
        pageBean.setPageSize(pageSize);

        int index = sql.indexOf("from");
        String tempSql = "select " + wrapper.getCountQuery() + " " + sql.substring(index);
        Long totalCount = countSql(tempSql, params);
        if (totalCount == null || totalCount == 0) {
            pageBean.setTotalCount(0L);
            return pageBean;
        }
        pageBean.setTotalCount(totalCount);

        if (pageNum != null && pageSize != null) {
            if (pageNum <= 0) {
                pageNum = 1;
            }
            sql += " limit " + (pageNum - 1) * pageSize + "," + pageSize;
        }
        Query query = entityManager.createNativeQuery(sql);
        if (params != null && params.size() > 0) {
            int length = params.size();
            for (String key : params.keySet()) {
                query.setParameter(key, params.get(key));
            }
        }
        List<Map<String, Object>> result = query.unwrap(org.hibernate.SQLQuery.class)
                .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        pageBean.setDatas(result);
        return pageBean;
    }

    /**
     * 执行hql查询
     *
     * @param hql
     * @param params
     * @return
     */
    public List<T> findHql(String hql, Map<String, Object> params) {
        //获取数据
        Query query = entityManager.createQuery(hql);
        setQueryParameters(query, params);
        List<T> records = query.getResultList();
        return records;
    }

    public List<T> findHqlLimit(String hql, Map<String, Object> params,Integer start,Integer pageSize) {
        //获取数据
        Query query = entityManager.createQuery(hql);
        query.setFirstResult(start);
        query.setMaxResults(pageSize);
        setQueryParameters(query, params);
        List<T> records = query.getResultList();
        return records;
    }

    /**
     * 使用Limit执行hql查询
     *
     * @param hql
     * @param params
     * @param firstResult 第一个元素位置（0开始）
     * @param maxResult   最多几个元素
     * @return
     */
    public List<T> findHqlForLimit(String hql, Map<String, Object> params, int firstResult, int maxResult) {
        //获取数据
        Query query = entityManager.createQuery(hql).setFirstResult(firstResult).setMaxResults(maxResult);
        setQueryParameters(query, params);
        List<T> records = query.getResultList();
        return records;
    }

    /**
     * 执行hql分页查询
     *
     * @param wrapper
     * @return
     */
    public PageBean<T> pageHql(Wrapper wrapper) {
        String hql = wrapper.getQuery();
        Map<String, Object> params = wrapper.getParams();
        PageBean searchPageBean = wrapper.getPageBean();
        Integer pageNum = searchPageBean.getPageNum();
        Integer pageSize = searchPageBean.getPageSize();
        //执行count操作
        PageBean<T> pageBean = new PageBean<>();
        pageBean.setPageNum(pageNum);
        pageBean.setPageSize(pageSize);
        int index = hql.indexOf("from");
        String tempHql = "select " + wrapper.getCountQuery() + " " + hql.substring(index);
        Long totalCount = countHql(tempHql, params);
        if (totalCount == null || totalCount == 0) {
            pageBean.setTotalCount(0L);
            return pageBean;
        }
        pageBean.setTotalCount(totalCount);

        //获取数据
        Query query = entityManager.createQuery(hql);
        if (params != null && params.size() > 0) {
            int length = params.size();
            for (String key : params.keySet()) {
                query.setParameter(key, params.get(key));
            }
        }
        if (pageNum != null && pageSize != null) {
            if (pageNum == 0) {
                pageNum = 1;
            }
            query.setFirstResult((pageNum - 1) * pageSize);
            query.setMaxResults(pageSize);
        }
        List<T> records = query.getResultList();
        pageBean.setDatas(records);
        return pageBean;
    }

    /**
     * 执行hql，查询数量
     *
     * @param hql
     * @param params
     * @return
     */
    public Long countHql(String hql, Map<String, Object> params) {
        String newHql = removeFetch(removeOrders(hql));
        Query query = entityManager.createQuery(newHql);
        setQueryParameters(query, params);
        return ((Long) query.getSingleResult());
    }

    public static String removeOrders(String sql) {
        Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", 2);
        Matcher m = p.matcher(sql);
        StringBuffer sb = new StringBuffer();

        while (m.find()) {
            m.appendReplacement(sb, "");
        }

        m.appendTail(sb);
        return sb.toString();
    }

    public static String removeFetch(String hql) {
        Pattern p = Pattern.compile("join\\s*fetch*", 2);
        Matcher m = p.matcher(hql);
        StringBuffer sb = new StringBuffer();

        while (m.find()) {
            m.appendReplacement(sb, "join");
        }

        m.appendTail(sb);
        return sb.toString();
    }

    /**
     * 为Query设置参数
     *
     * @param count
     * @param query
     * @param params
     */
    private void setQueryParameters(Query query, Map<String, Object> params) {
        if (params != null) {
            if (params.size() > 0) {
                int length = params.size();
                for (String key : params.keySet()) {
                    query.setParameter(key, params.get(key));
                }
            }
        }
    }

    /**
     * 执行sql，查询数量
     *
     * @param sql
     * @param params
     * @return
     */
    public Long countSql(String sql, Map<String, Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        setQueryParameters(query, params);
        return ((BigInteger) query.getSingleResult()).longValue();
    }

    public int updateNotNull(T bean) {
        try {
            batchUpdateNotNull(Arrays.asList(bean));
        } catch (Exception e) {
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>:error:" + e.getMessage() + "" + e.getCause());
            return -1;
        }
        return 1;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public int deleteEqualField(String field, Object value) {
        Query query = entityManager.createQuery("delete from " + clazz.getSimpleName() + " p where p." + field + " = ?1");
        query.setParameter(1, value);
        return query.executeUpdate();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public int deleteLikeField(String field, String value) {
        Query query = entityManager.createQuery("delete from " + clazz.getSimpleName() + " p where p." + field + " like ?1");
        query.setParameter(1, value);
        return query.executeUpdate();
    }

    public List<T> findEqualField(String field, Object value) {
        Query query = entityManager.createQuery("select p from " + clazz.getSimpleName() + " p where p." + field + " = ?1");
        query.setParameter(1, value);
        List<T> records = query.getResultList();
        return records;
    }

    public List<T> findLikeField(String field, String value) {
        Query query = entityManager.createQuery("select p from " + clazz.getSimpleName() + " p where p." + field + " like ?1");
        query.setParameter(1, value);
        List<T> records = query.getResultList();
        return records;
    }
}
